package org.hy.corn_task.schedule;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import okhttp3.Headers;
import org.hy.corn_task.enums.RequestStatus;
import org.hy.corn_task.pojo.StatisticRecord;
import org.hy.corn_task.pojo.entity.master.FailedLog;
import org.hy.corn_task.pojo.entity.master.TaskLog;
import org.hy.corn_task.pojo.entity.master.TaskRetry;
import org.hy.corn_task.pojo.entity.source.Dept;
import org.hy.corn_task.service.master.FailedLogService;
import org.hy.corn_task.service.master.TaskLogService;
import org.hy.corn_task.service.master.TaskRetryService;
import org.hy.corn_task.service.source.DeptService;
import org.hy.corn_task.service.target.YsgzBdcR1Service;
import org.hy.corn_task.service.target.YsgzBdcR2Service;
import org.hy.corn_task.utils.ExeTask;
import org.hy.corn_task.utils.SaveReqData;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 定时任务调度器
 */
@Component
@RequiredArgsConstructor
public class Schedule {

    /**
     * 批量保存最大数量
     */
    @Value("${data.save-batch-size}")
    private Integer SAVE_BATCH_SIZE;
    /**
     * 请求 URL
     */
    @Value("${data.url}")
    private String url;
    /**
     * 请求头
     */
    private static final Headers header = new Headers.Builder()
            .add("Content-Type", "application/x-www-form-urlencoded")
            .add("apikey", "pizqxE4BORUR6xje6CseWkjLB6aegrxS")
            .build();

    /**
     * 数据源表服务
     */
    private final DeptService deptService;
    /**
     * 系统服务-失败任务日志服务
     */
    private final FailedLogService failedLogService;
    /**
     * 系统服务-失败任务重试服务
     */
    private final TaskRetryService taskRetryService;
    /**
     * 保存服务
     */
    private final SaveReqData saveData;
    /**
     * 目标表 1 服务
     */
    private final YsgzBdcR1Service ysgzBdcR1Service;
    /**
     * 目标表 2 服务
     */
    private final YsgzBdcR2Service ysgzBdcR2Service;
    /**
     * 任务日志记录
     */
    private final TaskLogService taskLogService;

    /**
     * 标致变量-是否使用目标表 1
     */
    private static final AtomicBoolean isDB1 = new AtomicBoolean(true);
    /**
     * 标致变量-任务数量
     */
    private static final AtomicInteger taskNum = new AtomicInteger(0);
    /**
     * 重试任务数量
     */
    private static final AtomicInteger reTaskNum = new AtomicInteger(0);

    /**
     * 定时任务调度
     */
//    @Async("my-task-pool")
    @Scheduled(cron = "${data.exe-rule}")
    protected void scheduleTask() {
        System.out.println("任务开始：" + Thread.currentThread().getName() + "=====test值：" + taskNum.get());
        if (taskNum.get() < 0) {
            taskNum.set(0);
        }
        if (taskNum.get() >= 1) {
            System.out.println("任务跳过：" + Thread.currentThread().getName() + "=====test值：" + taskNum.get());
            return;
        }
        taskNum.set(taskNum.get() + 1);
        boolean currentTable = isDB1.get();
        System.out.println(currentTable? "表1" : "表2");
        isDB1.set(!isDB1.get());
        System.out.println("任务执行中：" + Thread.currentThread().getName() + "=====test值：" + taskNum.get());

        System.out.println("=====" + new Date() + "任务启动=====" + Thread.currentThread().getName());

        // 执行时间
        Date now = new Date();
        // 任务日志记录
        TaskLog taskLog = new TaskLog();
        // 任务操作的目标表
        String targetTable;
        // 保存任务统计数据
        List<StatisticRecord> statisticRecords = new ArrayList<>();

        // 本次任务需要插入的目标表，记录表名，并清空表数据
        if (currentTable) {
            targetTable = "ysgzBdcR1";
            ysgzBdcR1Service.remove(new QueryWrapper<>());
        } else {
            targetTable = "ysgzBdcR2";
            ysgzBdcR2Service.remove(new QueryWrapper<>());
        }

        // 查询重试表，第二次任务开始前强制停止第一次定时任务的重试任务
        QueryWrapper<TaskRetry> taskRetryQueryWrapper = new QueryWrapper<>();
        LambdaQueryWrapper<TaskRetry> eq = taskRetryQueryWrapper.lambda().eq(TaskRetry::getStatus, "1");
        List<TaskRetry> taskList = taskRetryService.list(eq);
        if (CollUtil.isNotEmpty(taskList)) {
            for (TaskRetry taskRetry : taskList) {
                taskRetry.setStatus("2");
            }
            taskRetryService.updateBatchById(taskList);
        }

        // 查询参数表，获取参数进行请求
        List<Dept> deptList = deptService.list();
        if (CollUtil.isEmpty(deptList)) {
            return;
        }

        taskLog.setTaskStartTime(now);
        taskLog.setCreateTime(now);
        taskLog.setTaskTargetTable(targetTable);
        taskLog.setWillRequestCount(deptList.size());
        taskLogService.save(taskLog);

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10, // 核心线程数
                20, // 最大线程数
                60, // 空闲线程存活时间（单位：秒）
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), // 任务队列
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        CountDownLatch countDownLatch = new CountDownLatch(200);

        Dept dept = new Dept();
        dept.setDeptName("手机归属地查询");
        for (int i = 0; i < 200; i++) {
//            for (Dept dept : deptList) {
            threadPoolExecutor.execute(() -> {
                StatisticRecord logRecord = new StatisticRecord();
                // 失败日志ID / 重试ID
                Integer logId = null;
                Integer retryId = null;
                String status;
                try {
                    ExeTask exeTask = new ExeTask();
                    JSONObject listTestOb = exeTask.exeAPITask(url, header, dept);

                    saveData.validData(listTestOb, logRecord);

                    if (currentTable) {
                        saveData.saveData1(listTestOb, SAVE_BATCH_SIZE);
                    } else {
                        saveData.saveData2(listTestOb, SAVE_BATCH_SIZE);
                    }
                } catch (Exception e) {
                    // 记录失败日志
                    FailedLog log = new FailedLog();
                    log.setTableNo(currentTable ? 1 : 2);
                    log.setTaskType("0");
                    log.setTaskId(null);
                    log.setExeTime(now);
                    log.setErrorMsg(e.getMessage());
                    log.setCreateTime(new Date());
                    failedLogService.save(log);
                    // 失败重试
                    String queryStr = JSONUtil.toJsonStr(dept);
                    TaskRetry retry = new TaskRetry();
                    retry.setTableNo(currentTable ? 1 : 2);
                    retry.setQueryMsg(queryStr);
                    retry.setOrderNum(1);
                    retry.setCreateTime(new Date());
                    taskRetryService.save(retry);

                    logId = log.getId();
                    retryId = retry.getId();

                } finally {
                    // 任务统计日志
//                    if (StrUtil.isBlank(logRecord.getStatus())) {
//                        logRecord.setStatus(RequestStatus.FAIL.getCode());
//                        logRecord.setRemark("未能正确捕获错误！");
//                    }
//                    if (StrUtil.isNotBlank(logRecord.getStatus())) {
//                        logRecord.setQuery(dept.getDeptName()); // 或者取 Name
//                        logRecord.setRetryId(retryId);
//                        logRecord.setLogId(logId);
//                        try {
//                            statisticRecords.add(logRecord);
//                        } catch (Exception e) {
//                            e.printStackTrace();
//                        }
//                    }
                    countDownLatch.countDown();
                }
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // todo: 这里需要验证一下：是不是多线程任务全部执行完后再执行这里的代码
        //任务完成关闭线程池
        threadPoolExecutor.shutdown();
        // 保存本次任务的日志
        taskLog.setTaskEndTime(new Date());
        saveTaskLog(taskLog, statisticRecords);
        taskNum.set(taskNum.get() - 1);
        System.out.println("任务结束：" + Thread.currentThread().getName() + "=====test值：" + taskNum.get());
        //任务结束，立即执行一次重试任务
        this.reScheduleTask();
    }

//    @PostConstruct
    public void initialize() {
        Thread thread = new Thread(this::scheduleTask);
        thread.start();
    }

    /**
     * 定时任务调度-重试任务
     */
    @Scheduled(cron = "${data.re-exe-rule}")
    protected void reScheduleTask() {
        System.out.println("重试任务开始===="+Thread.currentThread().getName());
        //有重试任务执行中，跳过新任务不执行
        if (reTaskNum.get() >= 1){
            return;
        }
        reTaskNum.set(reTaskNum.get()+1);
        System.out.println("重试任务执行中===="+Thread.currentThread().getName());
        Date now = new Date();
        LambdaQueryWrapper<TaskRetry> lqw = new LambdaQueryWrapper<>();
        lqw.eq(TaskRetry::getStatus, "1");
        lqw.orderByDesc(TaskRetry::getOrderNum);
        lqw.orderByAsc(TaskRetry::getCreateTime);
        List<TaskRetry> list = taskRetryService.list(lqw);

        if (CollUtil.isEmpty(list)) {
            return;
        }
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10, // 核心线程数
                20, // 最大线程数
                60, // 空闲线程存活时间（单位：秒）
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), // 任务队列
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        CountDownLatch countDownLatch = new CountDownLatch(list.size());

        for (TaskRetry retry : list) {
            threadPoolExecutor.execute(() -> {
                try {
                    Dept dept = JSONUtil.toBean(retry.getQueryMsg(), Dept.class);
                    //重试请求
                    ExeTask exeTask = new ExeTask();
                    JSONObject listTestOb = exeTask.exeAPITask(url, header, dept);
                    System.out.println(retry.getTableNo() + "表重试=========");
                    if (retry.getTableNo() == 1) {
                        saveData.saveData1(listTestOb, SAVE_BATCH_SIZE);
                    } else {
                        saveData.saveData2(listTestOb, SAVE_BATCH_SIZE);
                    }
                    retry.setReCount(retry.getReCount() + 1);
                    retry.setStatus("0");
                    retry.setUpdateTime(new Date());
                    taskRetryService.updateById(retry);
                } catch (Exception e) {
                    FailedLog log = new FailedLog();
                    log.setTableNo(retry.getTableNo());
                    log.setTaskType("1");
                    log.setTaskId(retry.getId());
                    log.setExeTime(now);
                    log.setErrorMsg(e.getMessage());
                    log.setCreateTime(new Date());
                    failedLogService.save(log);

                    TaskRetry updateRetry = new TaskRetry();
                    updateRetry.setId(retry.getId());
                    updateRetry.setReCount(retry.getReCount() + 1);
                    updateRetry.setUpdateTime(new Date());
                    taskRetryService.updateById(updateRetry);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //任务完成关闭线程池
        threadPoolExecutor.shutdown();
        reTaskNum.set(reTaskNum.get() - 1);
        System.out.println("重试任务结束===="+Thread.currentThread().getName());
    }

    /**
     * 保存任务日志记录
     *
     * @param taskLog          日志对象
     * @param statisticRecords 每次请求统计日志记录
     */
    private void saveTaskLog(TaskLog taskLog, List<StatisticRecord> statisticRecords) {
        // 请求成功的数量
        int succeedRequestCount = 0;
        int emptyRequestCount = 0;
        int failedRequestCount = 0;
        int dataCount = 0;
        int saveSucceedCount = 0;
        int saveFailedCount = 0;
        List<StatisticRecord> emptyRequestMsg = new ArrayList<>();
        List<StatisticRecord> failedRequestMsg = new ArrayList<>();
        List<StatisticRecord> saveFailedMsg = new ArrayList<>();

        // 处理每个请求的日志记录信息
        for (StatisticRecord record : statisticRecords) {
            // 请求查询为空的
            if (RequestStatus.EMPTY.getCode().equals(record.getStatus())) {
                emptyRequestCount++;
                emptyRequestMsg.add(record);
            }
            // 请求失败的
            else {
                failedRequestCount++;
                failedRequestMsg.add(record);
            }
        }

        // 封装任务的日志记录
        taskLog.setSucceedRequestCount(succeedRequestCount);
        taskLog.setEmptyRequestCount(emptyRequestCount);
        taskLog.setEmptyRequestMsg(CollUtil.isEmpty(emptyRequestMsg) ? null : JSONUtil.toJsonStr(emptyRequestMsg));
        taskLog.setFailedRequestCount(failedRequestCount);
        taskLog.setFailedRequestMsg(CollUtil.isEmpty(failedRequestMsg) ? null : JSONUtil.toJsonStr(failedRequestMsg));
        taskLog.setDataCount(dataCount);
        taskLog.setSaveSucceedCount(saveSucceedCount);
        taskLog.setSaveFailedCount(saveFailedCount);
        taskLog.setSaveFailedMsg(CollUtil.isEmpty(saveFailedMsg) ? null : JSONUtil.toJsonStr(saveFailedMsg));
        taskLogService.updateById(taskLog);
    }
}
